using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if MYCODE
using BepuUtilities.Vectors;
#else
using System.Numerics;
#endif

namespace BepuUtilities
{
    /// <summary>
    /// 提供类似XNA的轴对齐边界框功能。
    /// </summary>
    // 注意：显式大小可避免https://github.com/dotnet/coreclr/issues/12950
    [StructLayout(LayoutKind.Explicit, Size = 32)] 
    public struct BoundingBox
    {
        /// <summary>
        /// 轴对齐边界框中具有最低X、Y和Z坐标的位置。
        /// </summary>
        [FieldOffset(0)]
        public Vector3 Min;

        /// <summary>
        /// 轴对齐边界框中具有最高X、Y和Z坐标的位置。
        /// </summary>
        [FieldOffset(16)]
        public Vector3 Max;

        /// <summary>
        /// 从指定的最小值和最大值构造边界框。
        /// </summary>
        /// <param name="min">轴对齐边界框包含的最低X、Y和Z坐标的位置。</param>
        /// <param name="max">轴对齐边界框包含的最高X、Y和Z坐标的位置。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public BoundingBox(Vector3 min, Vector3 max)
        {
            this.Min = min;
            this.Max = max;
        }


        /// <summary>
        /// 确定一个边框是否与另一个边框相交。
        /// </summary>
        /// <param name="a">要测试的第一个边框。</param>
        /// <param name="b">要测试的第二个边框。</param>
        /// <return>边界框是否相交。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool Intersects(in BoundingBox a, in BoundingBox b)
        {
            return Intersects(a.Min, a.Max, b.Min, b.Max);
        }
        // TODO：在过去的某个时候,人们发现使用非短路操作符的交叉口会更快。
        // 虽然这确实有一定的意义(相对于它们的成本,分支并不是真正有价值的),但是仍然有足够的问题需要在现代编译器上重新计算。
        /// <summary>
        /// 确定一个边框是否与另一个边框相交。
        /// </summary>
        /// <param name="a">要测试的第一个边框。</param>
        /// <param name="b">要测试的第二个边框。</param>
        /// <return>边界框是否相交。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool Intersects(in Vector3 minA, in Vector3 maxA, in Vector3 minB, in Vector3 maxB)
        {
            return maxA.X >= minB.X & maxA.Y >= minB.Y & maxA.Z >= minB.Z &
                   maxB.X >= minA.X & maxB.Y >= minA.Y & maxB.Z >= minA.Z;
        }

        /// <summary>
        /// 计算边界框的体积。
        /// </summary>
        /// <param name="box">要测量的边框。</param>
        /// <return>边界框的音量。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe float ComputeVolume(ref BoundingBox box)
        {
            var diagonal = (box.Max - box.Min);
            return diagonal.X * diagonal.Y * diagonal.Z;
        }


        /// <summary>
        /// 计算包含另外两个边界框的边界框。
        /// </summary>
        /// <param name="Mina">要合并的第一个边框的最小值。</param>
        /// <param name="Maxa">要合并的第一个边框的最大值。</param>
        /// <param name="minB">要合并的第二个边框的最小值。</param>
        /// <param name="maxB">要合并的第二个边框的最大值。</param>
        /// <param name="min">合并边界框的最小值。</param>
        /// <param name="max">合并边界框的最大值。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void CreateMerged(in Vector3 minA, in Vector3 maxA, in Vector3 minB, in Vector3 maxB, out Vector3 min, out Vector3 max)
        {
            min = Vector3.Min(minA, minB);
            max = Vector3.Max(maxA, maxB);
        }

        /// <summary>
        /// 计算包含另外两个边界框的边界框。
        /// </summary>
        /// <param name="a">要包含的第一个边界框。</param>
        /// <param name="b">要包含的第二个边框。</param>
        /// <param name="merded">包含两个输入框的边界框。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void CreateMerged(in BoundingBox a, in BoundingBox b, out BoundingBox merged)
        {
            CreateMerged(a.Min, a.Max, b.Min, b.Max, out merged.Min, out merged.Max);
        }

        /// <summary>
        /// 确定边界框是否与边界球体相交。
        /// </summary>
        /// <param name="binding ingSphere">要测试交叉点的球体。</param>
        /// <return>边框形状是否相交。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool Intersects(ref BoundingSphere boundingSphere)
        {
            var offset = boundingSphere.Center - Vector3.Min(Vector3.Max(boundingSphere.Center, Min), Max);
            return Vector3.Dot(offset, offset) <= boundingSphere.Radius * boundingSphere.Radius;

        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ContainmentType Contains(ref BoundingBox boundingBox)
        {
            if (Max.X < boundingBox.Min.X || Min.X > boundingBox.Max.X ||
                Max.Y < boundingBox.Min.Y || Min.Y > boundingBox.Max.Y ||
                Max.Z < boundingBox.Min.Z || Min.Z > boundingBox.Max.Z)
                return ContainmentType.Disjoint;
            // 众所周知,它至少是相交的。它被控制住了吗？
            if (Min.X <= boundingBox.Min.X && Max.X >= boundingBox.Max.X &&
                Min.Y <= boundingBox.Min.Y && Max.Y >= boundingBox.Max.Y &&
                Min.Z <= boundingBox.Min.Z && Max.Z >= boundingBox.Max.Z)
                return ContainmentType.Contains;
            return ContainmentType.Intersects;
        }



        /// <summary>
        /// 创建包含点列表的尽可能小的边框。
        /// </summary>
        /// <param name="Points">要用边界框包围的点。</param>
        /// <return>包含点列表的边框。</return>
        public static BoundingBox CreateFromPoints(IList<Vector3> points)
        {
            BoundingBox aabb;
            if (points.Count == 0)
                throw new Exception("Cannot construct a bounding box from an empty list.");
            aabb.Min = points[0];
            aabb.Max = aabb.Min;
            for (int i = points.Count - 1; i >= 1; i--)
            {
                aabb.Min = Vector3.Min(points[i], aabb.Min);
                aabb.Max = Vector3.Max(points[i], aabb.Max);
            }
            return aabb;
        }




        /// <summary>
        /// 从边界球体创建边界框。
        /// </summary>
        /// <param name="binding ingSphere">要用于创建边界框的边界球体。</param>
        /// <param name="binding ingBox">从边界球体创建的边界框。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void CreateFromSphere(ref BoundingSphere boundingSphere, out BoundingBox boundingBox)
        {
            var radius = new Vector3(boundingSphere.Radius);
            boundingBox.Min = boundingSphere.Center - radius;
            boundingBox.Max = boundingSphere.Center + radius;
        }

        /// <summary>
        /// 创建边界框的字符串表示形式。
        /// </summary>
        /// <return>边界框的字符串表示形式。</return>
        public override string ToString()
        {
            return $"({Min.ToString()}, {Max.ToString()})";
        }

    }
}
